#include "Directory.h"
#include "File.h"
#include "Exception.h"

using namespace Woo;
using namespace Woo::IO;

void Directory::CreateDirectory(string path)
{
	Directory::CreateDirectory(path, S_IRWXU|S_IRWXG|S_IRWXO);
}

void Directory::CreateDirectory(string path, mode_t mode)
{
	if(path.empty())
		throw ArgumentException("path is empty.", __FILE__, __LINE__);

	path = Directory::FixPathString(path);
	string::size_type pos, start = 1;
	while((pos = path.find("/", start)) != string::npos)
	{
		string tmp = path.substr(0, pos);
		if(!Directory::Exists(tmp))
			if(mkdir(tmp.c_str(), mode) == -1)
				throw IOException(strerror(errno), __FILE__, __LINE__);
		start = pos + 1;
	}
}

void Directory::Delete(string path)
{
	if(path.empty())
		throw ArgumentException("path is empty.", __FILE__, __LINE__);

	struct stat info;
	if(stat(path.c_str(), (struct stat*)&info) == -1)
		if(errno == ENOENT)
			throw DirectoryNotFoundException(strerror(errno), __FILE__, __LINE__);
		else
			throw IOException(strerror(errno), __FILE__, __LINE__);

	if(!S_ISDIR(info.st_mode))
		throw IOException("path is not directory.", __FILE__, __LINE__);

	vector<string> subDirectory = Directory::GetDirectories(path);
	vector<string> files = Directory::GetFiles(path);
	for(vector<string>::iterator iter=files.begin();
		iter!=files.end();
		++iter)
			File::Delete(*iter);

	for(vector<string>::iterator iter=subDirectory.begin();
		iter!=subDirectory.end();
		++iter)
			Directory::Delete(*iter);

	if(rmdir(path.c_str()) == -1)
		throw IOException(strerror(errno), __FILE__, __LINE__);
}

bool Directory::Exists(string path)
{
	if(path.empty())
		throw ArgumentException("path is empty.", __FILE__, __LINE__);

	struct stat info;
	if(stat(path.c_str(), (struct stat*)&info) == -1)
		return false;
	else
		return true;
}

vector<string> Directory::GetDirectories(string path)
{
	if(path.empty())
		throw ArgumentException("path is empty.", __FILE__, __LINE__);

	path = Directory::FixPathString(path);
	vector<string> subdir;
	DIR* dir = opendir(path.c_str());
	if(dir == 0)
		if(errno == ENOENT || errno == ENOTDIR)
			throw DirectoryNotFoundException(strerror(errno), __FILE__, __LINE__);
		else
			throw IOException(strerror(errno), __FILE__, __LINE__);

	struct dirent* pItem = 0;
	while((pItem = readdir(dir)) != 0)
	{
		if(errno == EBADF)
			throw IOException(strerror(errno), __FILE__, __LINE__);

		if(strcmp(pItem->d_name, ".") == 0 || strcmp(pItem->d_name, "..") == 0 )
			continue;

		string tmp(path);
		tmp.append(pItem->d_name);

		struct stat info;
		if(stat(tmp.c_str(), (struct stat*)&info) == -1)
			throw IOException(strerror(errno), __FILE__, __LINE__);

		if(S_ISDIR(info.st_mode))
		{
			tmp.append("/");
			subdir.push_back(tmp);
		}

	}
	closedir(dir);
	return subdir;
}

vector<string> Directory::GetFiles(string path)
{
	if(path.empty())
		throw ArgumentException("path is empty.", __FILE__, __LINE__);
		
	path = Directory::FixPathString(path);
	vector<string> files;
	DIR* dir = opendir(path.c_str());
	if(dir == 0)
		if(errno == ENOENT || errno == ENOTDIR)
			throw DirectoryNotFoundException(strerror(errno), __FILE__, __LINE__);
		else
			throw IOException(strerror(errno), __FILE__, __LINE__);

	struct dirent* pItem = 0;
	while((pItem = readdir(dir)) != 0)
	{
		if(errno == EBADF)
			throw IOException(strerror(errno), __FILE__, __LINE__);

		if(strcmp(pItem->d_name, ".") == 0 || strcmp(pItem->d_name, "..") == 0 )
			continue;

		string tmp(path);
		tmp.append(pItem->d_name);

		struct stat info;
		if(stat(tmp.c_str(), (struct stat*)&info) == -1)
			throw IOException(strerror(errno), __FILE__, __LINE__);

		if(!S_ISDIR(info.st_mode))
		{
			files.push_back(tmp);
		}
	}
	closedir(dir);
	return files;
}

void Directory::Move(string sourceDirName, string destDirName)
{
	if(sourceDirName.empty())
		throw ArgumentException("source path is empty.", __FILE__, __LINE__);

	if(destDirName.empty())
		throw ArgumentException("destination path is empty.", __FILE__, __LINE__);

	if(rename(sourceDirName.c_str(), destDirName.c_str()) == -1)
	{
		if(errno == ENOENT)
			throw DirectoryNotFoundException(strerror(errno), __FILE__, __LINE__);
		else
			throw IOException(strerror(errno), __FILE__, __LINE__);
	}
}

void Directory::Copy(string sourceDirName, string destDirName)
{
	Directory::Copy(sourceDirName, destDirName, true);
}

void Directory::Copy(string sourceDirName, string destDirName, bool recursive)
{
	if(sourceDirName.empty())
		throw ArgumentException("source path is empty.", __FILE__, __LINE__);

	if(destDirName.empty())
		throw ArgumentException("destination path is empty.", __FILE__, __LINE__);
	
	sourceDirName = Directory::FixPathString(sourceDirName);
	destDirName = Directory::FixPathString(destDirName);

	struct stat info;
	if(stat(sourceDirName.c_str(), (struct stat*)&info) == -1)
		if(errno == ENOENT)
			throw DirectoryNotFoundException(strerror(errno), __FILE__, __LINE__);
		else
			throw IOException(strerror(errno), __FILE__, __LINE__);

	if(!S_ISDIR(info.st_mode))
		throw IOException("source path is not directory.", __FILE__, __LINE__);

	if(stat(destDirName.c_str(), (struct stat*)&info) == -1)
	{
		Directory::CreateDirectory(destDirName);
	}
	else
	{
		if(!S_ISDIR(info.st_mode))
			throw IOException("destination path is not directory.", __FILE__, __LINE__);
	}

	vector<string> files = Directory::GetFiles(sourceDirName);
	for(vector<string>::iterator iter=files.begin();
		iter!=files.end();
		++iter)
	{
		string tmp = destDirName;
		string::size_type pos;
		if((pos = iter->find_last_of("/")) == string::npos)
			tmp.append(*iter);
		else
			tmp.append(iter->substr(pos + 1));
		File::Copy(*iter, tmp);
	}

	if(recursive)
	{
		vector<string> subDirectory = Directory::GetDirectories(sourceDirName);
		for(vector<string>::iterator iter=subDirectory.begin();
			iter!=subDirectory.end();
			++iter)
		{
			string tmp = destDirName;
			string::size_type pos;
			if((pos = iter->substr(0, iter->length() - 1).find_last_of("/")) == string::npos)
				tmp.append(*iter);
			else
				tmp.append(iter->substr(pos + 1));
			Directory::Copy(*iter, tmp);
		}
	}
}

void Directory::CreateSymLink(string sourceDirName, string destDirName)
{
	if(sourceDirName.empty())
		throw ArgumentException("source path is empty.", __FILE__, __LINE__);

	if(destDirName.empty())
		throw ArgumentException("destination path is empty.", __FILE__, __LINE__);

	if(symlink(sourceDirName.c_str(), destDirName.c_str()) == -1)
	{
		throw IOException(strerror(errno), __FILE__, __LINE__);
	}
}

string Directory::FixPathString(string path)
{
	if(path.empty())
		return path;
		
	string fix;
	string::size_type pos, start = 0;
	while((pos = path.find("\\", start))!= string::npos)
	{
		fix.append(path.substr(start, pos));
		fix.append("/");
		start = pos + 1;
	}
	fix.append(path.substr(start));

	if(fix.at(path.length()-1) != '/')
		fix.append("/");
	return fix;
}
